'use strict';
var _ = require('lodash');
var quick = require('quick-pomelo');
var P = quick.Promise;
var cor = P.coroutine;
var _ = require('lodash');
var uuid = require('node-uuid');
var Logic = require('./logic');
var User = require('./user');
var Score = require('./score');
var conf = require('../config/games').lbmajiang || {};
var M = require('../../share/message');
var C = require('../../share/constant');
var logger = quick.logger.getLogger('table', __filename);
// 所有牌
const CARDS = [
    11, 12, 13, 14, 15, 16, 17, 18, 19,			// 筒
    21, 22, 23, 24, 25, 26, 27, 28, 29,			// 万
    31, 32, 33, 34, 35, 36, 37, 38, 39,			// 条
    41, 42, 43, 44,								// 风
    51, 52, 53,								    // 字
    11, 12, 13, 14, 15, 16, 17, 18, 19,			// 筒
    21, 22, 23, 24, 25, 26, 27, 28, 29,			// 万
    31, 32, 33, 34, 35, 36, 37, 38, 39,			// 条
    41, 42, 43, 44,								// 风
    51, 52, 53,								    // 字
    11, 12, 13, 14, 15, 16, 17, 18, 19,			// 筒
    21, 22, 23, 24, 25, 26, 27, 28, 29,			// 万
    31, 32, 33, 34, 35, 36, 37, 38, 39,			// 条
    41, 42, 43, 44,								// 风
    51, 52, 53,								    // 字
    11, 12, 13, 14, 15, 16, 17, 18, 19,			// 筒
    21, 22, 23, 24, 25, 26, 27, 28, 29,			// 万
    31, 32, 33, 34, 35, 36, 37, 38, 39,			// 条
    41, 42, 43, 44,								// 风
    51, 52, 53,								    // 字
    61, 62, 63, 64, 65, 66, 67, 68              // 花   
];
// 块类型
const STYLE = {
    NULL: 0,				// 无效
    CHI: 1,					// 吃牌
    SUN: 2,	 			    // 顺序
    PENG: 3,	 			// 碰子
    KE: 4,				    // 刻子
    GANG: 5,				// 杠子
    ANGANG: 6,			    // 暗杠
    ZMGANG: 7               // 自摸明杠
};
// 椅子数
const CHAIR_COUNT = 4;
const CARDS_COUNT = 13;
// const LAIZI = 60;
// 构造方法
var Logic = function (type, lingbi) {
    // 类型
    this.type = type;
    // 七对
    this.has7d = true;    //this.has7d = !!(type & 1);
    // 全牌
    var cards = CARDS;
    //不带花
    if (!(lingbi & 4)) cards = cards.filter(card => card < 60);
    // 去字
    //    if (type & 2) cards = cards.filter(card => card < 40);
    this.jiaZhanghu = !!(lingbi & 1);
    this.castCount = 0;
    this.disCount = 0;
    this.lastCount = cards.length;
    this.cardsPool = cards;
    this.dahua = lingbi & 4;
    this.id = 0;
};
// 导出常量
Logic.CARDS = CARDS;
Logic.STYLE = STYLE;
Logic.CHAIR_COUNT = CHAIR_COUNT;
Logic.CARDS_COUNT = CARDS_COUNT;
// 导出类
module.exports = Logic;
// 原型对象
var proto = Logic.prototype;
// 重新洗牌
proto.shuffle = function () {
    this.castCount = 0;
    this.disCount = 0;
    this.lastCount = this.cardsPool.length;
    this.cardsPool = _.shuffle(this.cardsPool);
};
// 填充数组
proto.fillDeep = function (array, o, isMore) {
    for (let i = 0; i < array.length; ++i) {
        if (!isMore) {
            array[i] = _.clone(o);
        } else {
            array[i] = _.cloneDeep(o);
        }
    }
    return array;
};
// 获取类型
proto.getType = function (card) {
    return Math.floor(card / 10);
};
// 获取牌值
proto.getValue = function (card) {
    return card % 10;
};
//获取癞子个数
proto.getLaiziCount = function (Arr, laizi) {
    var laiziCount = 0;
    for (var i = 0; i < Arr.length; ++i) {
        if (Arr[i] == laizi) {
            laiziCount++;//癞子数量
        }
    }
    return laiziCount;
}
// 手牌排序
proto.sort = function (handCards) {
    return _.sortBy(handCards);
};
// 删除手牌
proto.remove = function (handCards, cards) {
    this.sort(handCards);
    if (typeof (cards) == 'number') {
        if (cards == handCards[handCards.length - 1]) {
            return (handCards.pop(), true);
        }
        else {
            let pos = handCards.indexOf(cards);
            if (pos == -1) {
                return false;
            }
            return (handCards.splice(pos, 1), true);
        }
    }
    var length = cards.length;
    if (length > 1 && cards[0] == cards[length - 1]) {
        if (cards[0] == handCards[handCards.length - 1]) {
            handCards.pop();
            length -= 1;
        }
        let pos = handCards.indexOf(cards[0]);
        if (pos == -1) return false;
        handCards.splice(pos, length);
    } else {
        for (let i = 0; i < length; ++i) {
            let pos = handCards.indexOf(cards[i]);
            if (pos == -1) return false;
            handCards.splice(pos, 1);
        }
    }
    return true;
};
// 调整手牌
proto.adjust = function (handCards) {
    var length = handCards.length;
    if (length < 2) return false;
    if (handCards[length - 1] < handCards[length - 2]) {
        let moCard = handCards.pop();
        let pos = _.findIndex(handCards, (i) => (i >= moCard));
        if (pos == -1) handCards.push(moCard);
        else handCards.splice(pos, 0, moCard);
    }
    return true;
};
// 投骰子
proto.dice = function () {
    return _.random(1, 6);
};
//随机出癞子
proto.laizi = function () {
    var card = CARDS[_.random(0, 135)];
    if (this.getType(card) == 5) this.laizi();
    return card;
}
//判断花牌
proto.isHua = function (card) {
    return ((card > 60 && card < 69) || (card < 54 && card > 50));//中白发算作花
}
// 余牌数
proto.leaveCount = function (disCount) {
    if (disCount >= 0) {
        this.lastCount = this.lastCount + this.disCount - disCount;
        this.disCount = disCount;
        if (this.lastCount < 0) this.lastCount = 0;
    }
    return this.lastCount;
};
// 初始手牌
proto.handCards = function (chBanker, tableId) {
    this.shuffle();
    var result = [];
    this.id = tableId;
    var castCount = 0;
    for (let i = 0; i < CHAIR_COUNT; ++i) {
        let cardCount = CARDS_COUNT;
        if (i == chBanker) cardCount += 1;
        let cards = this.cardsPool.slice(castCount, castCount + cardCount);
        castCount += cardCount;
        result.push({ cards: this.sort(cards) });
    }
    this.castCount += castCount;
    this.lastCount -= castCount;
    //!!!测试牌型=============================
    // var handCards = [];
    // handCards[chBanker] = [13, 13, 16, 16, 19, 19, 44, 44, 44, 53, 53, 53, 19,27];
    // handCards[(chBanker + 1) % 4] = [13, 13, 16, 16, 19, 19, 44, 44, 44, 53, 53, 53, 51];
    // handCards[(chBanker + 2) % 4] = [21, 22, 23, 24, 25, 26, 27, 28, 25, 27, 27, 25, 53];
    // handCards[(chBanker + 3) % 4] = [23, 29, 13, 14, 15, 16, 17, 18, 19, 19, 19, 11, 11];
    // for (var i = 0; i < 4; ++i) {
    //     result.push({ cards: this.sort(handCards[i]) });
    // }
    return result;
};
// 是否无牌
proto.isNoCards = function () {
    return this.lastCount <= 0;
};
// 摸牌
proto.moCard = function () {
    var card = 0;
    //   logger.info("桌子id ==" + this.id + ":剩下的牌数",this.lastCount);
    if (this.lastCount > 0) {
        card = this.cardsPool[this.castCount++];
        this.lastCount -= 1;
    }
    //   if(card>60) this.moCard();
    return { card: card };
};
// 是否将牌
proto.isJang = function (lcard, rcard) {
    return lcard == rcard;
};
// 可否碰牌
proto.isPeng = function (handCards, card) {
    for (let i = 0; i < handCards.length - 1; ++i) {
        if (handCards[i] == card && handCards[i + 1] == card) {
            return true;
        }
    }
    return false;
};
// 碰牌
proto.peng = function (handCards, card) {
    if (this.isPeng(handCards, card)) {
        return { style: STYLE.PENG, cards: [card, card, card], disc: [card] };
    }
};
// 吃牌，1-@**左吃, 2-*@*中吃, 3-@**右吃
proto.chi = function (handCards, card, type) {
    var chis = this.chiAnalyze(handCards, card, type);
    if (chis[0]) {
        let res = { style: STYLE.CHI, type: type, disc: [card] };
        if (type == 1) {
            res.cards = [card, card + 1, card + 2];
            if (this.getValue(card) < 7) {
                res.disc.push(card + 3);
            }
        } else if (type == 2) {
            res.cards = [card - 1, card, card + 1];
        } else {
            res.cards = [card - 2, card - 1, card];
            if (this.getValue(card) > 3) {
                res.disc.push(card - 3);
            }
        }
        return res;
    }
};
// 杠牌，1-普通杠, 2-暗杠, 3-自摸明杠
proto.gang = function (handCards, card, type) {
    // 结果
    var res = null;
    // 明杠
    if (type == 1) {
        let gang = this.gangAnalyze(handCards, card);
        if (gang) {
            res = { style: STYLE.GANG, cards: [card, card, card] };
        }
    }
    // 暗杠
    else if (type == 2) {
        let anGang = this.anGangAnalyze(handCards, card);
        if (anGang[0]) {
            res = { style: STYLE.ANGANG, cards: [card, card, card] };
        }
    }
    return res;
};
// 自摸明杠，1-普通杠, 2-暗杠, 3-自摸明杠
proto.zmGang = function (handCards, huCards, card) {
    var zmGang = this.zmGangAnalyze(handCards, huCards, card);
    if (zmGang[0]) {
        return { style: STYLE.ZMGANG, cards: [card, card, card] };
    }
};
// 吃牌分析，1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传
proto.chiAnalyze = function (handCards, card, type) {
    // 结果集合
    var result = [];
    // 排除风字
    if (handCards.length < 2 || card > 40) return result;
    // 查找句子
    var length = handCards.length;
    for (let i = 0; i < length; ++i) {
        // 牌面值
        let value = this.getValue(card);
        // @**左吃，吃类型(可以不指定)
        if (!type || type == 1) {
            if (value < 8 && i < length - 1) {
                if (handCards[i] == card + 1 && handCards[i + 1] == card + 2) {
                    result.push({ type: 1, card: card });
                }
            }
        }
        // *@*中吃，吃类型(可以不指定)
        if (!type || type == 2) {
            if (value > 1 && value < 9 && i < length - 1) {
                if (handCards[i] == card - 1 && handCards[i + 1] != card - 1) {
                    for (let j = i + 1; j < i + 5 && j < length; ++j) {
                        if (handCards[j] == card + 1) {
                            result.push({ type: 2, card: card });
                            break;
                        }
                    }
                }
            }
        }
        // **@右吃，吃类型(可以不指定)
        if (!type || type == 3) {
            if (value > 2 && i < length - 1) {
                if (handCards[i] == card - 2 && handCards[i + 1] == card - 1) {
                    result.push({ type: 3, card: card });
                }
            }
        }
    }
    return result;
};
// 普通杠分析，1-普通杠, 2-暗杠, 3-自摸明杠
proto.gangAnalyze = function (handCards, card) {
    // 结果对象
    var result = null;
    var length = handCards.length;
    if (this.dahua) {
        if (length < 3 || card > 50) return result;
    }
    else {
        if (length < 3) return result;
    }
    // 查找同牌
    for (let i = 0; i < handCards.length - 2; ++i) {
        if (handCards[i] == card && handCards[i + 1] == card && handCards[i + 2] == card) {
            result = { type: 1, card: card };
            // logger.info("桌子id ==" + this.id + ":gang 普通杠分析 card = %d ",card);
            break;
        }
    }
    return result;
};
// 暗杠分析，1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
proto.anGangAnalyze = function (handCards, card) {
    // 结果对象
    var result = [];
    var length = handCards.length;
    if (length < 4) return result;
    //带花，中发白不能 杠
    if (this.dahua && card > 50) return result;
    // 查找同牌
    var moCard = handCards[length - 1];
    for (let i = 0; i < length - 3; ++i) {
        // 指定杠牌，可以不指定
        if (card && handCards[i] != card) continue;
        // 判定是否刻子
        if (handCards[i] == handCards[i + 1] && handCards[i] == handCards[i + 2]) {
            // 手上有杠牌
            if (handCards[i] == handCards[i + 3]) {
                result.push({ type: 2, card: handCards[i] });
            }
            // 刚摸到杠牌
            else if (handCards[i] == moCard) {
                result.push({ type: 2, card: moCard });
            }
        }
    }
    return result;
};
// 自摸杠分析，1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
proto.zmGangAnalyze = function (handCards, huCards, card) {
    // 返回结果
    var result = [];
    // 遍历句子
    for (let i = 0; i < huCards.length; ++i) {
        if (huCards[i].style != STYLE.PENG) {
            continue;
        }
        if (this.dahua && card > 50) return result;
        let _card = huCards[i].cards[0];
        if (card && _card != card) continue;
        let pos = handCards.indexOf(_card);
        if (pos != -1) {
            result.push({ type: 3, card: _card });
            if (card) break;
        }
    }
    return result;
};
// 分析牌型
proto.parseBlock = function (card1, card2, card3) {
    // 刻子
    if (card1 && card1 == card2 && card1 == card3) {
        return { style: STYLE.KE, cards: [card1, card2, card3] };
    }
    // 顺子(排除风字)
    var cards = this.sort([card1, card2, card3]);
    if (cards[2] < 40) {
        if (cards[2] == cards[1] + 1 && cards[1] == cards[0] + 1) {
            return { style: STYLE.SUN, cards: cards };
        }
    }
};
// 提取成句
proto._dumpFixed = function (cards, huCards) {
    for (let i = 0; i < cards.length - 2; ++i) {
        let card = cards[i];
        if (card <= 0) continue;
        if (card == cards[i + 1] && card == cards[i + 2]) {
            huCards.push({ style: STYLE.KE, cards: [card, card, card] });
            cards[i] = 0; cards[i + 1] = 0; cards[i + 2] = 0;
            i += 2; continue;
        }
        if (card > 40 || this.getValue(card) > 7) continue;
        let second = -1, third = -1;
        for (let j = i + 1; j < cards.length; ++j) {
            if (cards[j] == card + 1) second = j;
            if (cards[j] == card + 2) third = j;
            if (cards[j] > card + 2) break;
            if (second != -1 && third != -1) break;
        }
        if (second != -1 && third != -1) {
            huCards.push({ style: STYLE.SUN, cards: [card, cards[second], cards[third]] });
            cards[i] = 0; cards[second] = 0; cards[third] = 0;
        }
    }
};
//听牌分析--不带癞子
proto.isTingpai = function (handcards, huCards) {
    var tingCards = [];
    for (var i = 0; i < handcards.length; ++i) {
        var card = handcards[i];
        var ting = [];
        for (var j = 0; j < 　31; ++j) {
            //将手牌中的牌依次替换，判断是否能胡牌
            handcards[i] = CARDS[j];
            if (this.jiaZhanghu) {
                if (this.jiaZhangHu(handcards, handcards[i], huCards, true)) ting.push(CARDS[j]);
            }
            else {
                if (this.huAnalyze(handcards, handcards[i], huCards, true) || this.isSevenDouble(handcards)) {
                    ting.push(CARDS[j]);
                }
            }
        }
        if (this.jiaZhanghu) {
            if (ting.length == 1) tingCards.push({ chu: card, ting: ting });
        }
        else {
            if (ting.length > 0) tingCards.push({ chu: card, ting: ting });
        }
        handcards[i] = card;
    }
    return tingCards;
}
// 胡牌分析--不带赖子
proto.huAnalyze = function (handCards, card, huCards, isTouch) {
    // 全部手牌
    if (!isTouch) handCards = handCards.concat(card);
    handCards = this.sort(handCards);
    var huRes = { card: card, jiang: 0, huCards: [] };
    // 重设将牌
    var resetHuRes = (jiang) => {
        huRes.jiang = jiang;
        huRes.huCards = [];
        for (let huCard of huCards) huRes.huCards.push(huCard);
    };
    // 手牌不全
    var length = handCards.length;
    if (handCards.length % 3 != 2 || handCards.length > 14) return;
    // 是否七对
    if (this.has7d && this.isSevenDouble(handCards))
        return (resetHuRes(-1), huRes);
    // 确定将牌
    for (let i = 0; i < handCards.length - 1; ++i) {
        if (!this.isJang(handCards[i], handCards[i + 1])) continue;
        resetHuRes(handCards[i]);
        let cards = handCards.slice(0, i);//拼接除开将外的牌
        cards = cards.concat(handCards.slice(i + 2));
        this._dumpFixed(cards, huRes.huCards);
        if (huRes.huCards.length >= 4) return huRes;
    }
};
proto.jiaZhangHu = function (handCards, card, huCards, isTouch) {
    if (this.pengHu(handCards, huCards)) return false;
    if (this.isSevenDouble(handCards)) return false;
    var huRes = this.huAnalyze(handCards, card, huCards, isTouch);
    if (huRes) {
        if (huRes.jiang == card) {
            return true;
        }
        if (card < 40) {
            for (let i = 0; i < huRes.huCards.length; ++i) {
                if (huRes.huCards[i].style == STYLE.SUN) {
                    if (huRes.huCards[i].cards[1] == card) return true;
                    if (this.getValue(card) == 3 && huRes.huCards[i].cards[2] == card) return true;
                    if (this.getValue(card) == 7 && huRes.huCards[i].cards[0] == card) return true;
                }
            }
        }
    }
    return false;
}
//判断碰碰胡
proto.pengHu = function (handcards, huCards, laizi) {
    // logger.info("桌子id ==" + this.id + ":碰碰胡hucard = [],handcards = []",huCards,handcards);
    handcards = this.sort(handcards);//排序    
    var pengCount = 0;
    if (handcards.length != 5) return false;
    for (var i = 0; i < huCards.length; ++i) {
        // logger.info("桌子id ==" + this.id + ":huCards[i].style=%d",huCards[i].style);
        if (huCards[i].style == STYLE.PENG || huCards[i].style == STYLE.GANG || huCards[i].style == STYLE.ANGANG || huCards[i].style == STYLE.ZMGANG) pengCount++;
    }
    if (pengCount == 3) {
        if (laizi < 0) {
            if ((handcards[0] == handcards[1] && handcards[2] == handcards[4]) || (handcards[0] == handcards[2] && handcards[3] == handcards[4]))
                return true;
        }
        else {
            var laizicount = this.getLaiziCount(handcards, laizi);
            if (laizicount == 0) {
                if ((handcards[0] == handcards[1] && handcards[2] == handcards[4]) || (handcards[0] == handcards[2] && handcards[3] == handcards[4])) return true;
            }
            else if (laizicount >= 3) return true;
            else {
                for (var i = 0; i < 5; ++i) {
                    if (i < 3 && handcards[i] == handcards[i + 1] && handcards[i] == handcards[i + 2]) return true;
                    if (i < 2 && handcards[i] == handcards[i + 1] && handcards[i + 2] == handcards[i + 3]) return true;
                }
                if (handcards[0] == handcards[1] && handcards[3] == handcards[4]) return true;
            }
        }
    }
    return false;
}
// 是否七对--不带癞子
proto.isSevenDouble = function (handCards) {
    if (handCards.length <= CARDS_COUNT) return false;
    var dcount = 0;
    var counts = {};
    for (let i = 0; i < handCards.length; ++i) {
        let card = handCards[i];
        counts[card] = (counts[card] || 0) + 1;
        if (counts[card] >= 2) {
            dcount += 1;
            counts[card] = 0;
        }
    }
    return (dcount >= 7);
};
proto.is7dadui = function (handcards, laizi) {
    var gangCount = 0;
    if (laizi <= 0) {
        if (this.isSevenDouble(handcards)) {
            for (let i = 0; i < 11; ++i) {
                if (handcards[i] == handcards[i + 3]) {
                    gangCount++;
                }
            }
        }
    }
    else {
        var laizicount = this.getLaiziCount(handcards, laizi);
        if (this.is_7_pair(handcards, laizi, laizicount)) {
            for (let i = 0; i < 11; ++i) {
                if (handcards[i] == laizi) continue;
                if (handcards[i] == handcards[i + 3]) gangCount++;
                else if (handcards[i] == handcards[i + 2]) {
                    gangCount++;
                    laizicount--;
                }
            }
        }
    }
    return gangCount;
}
//获取手牌中牌的数量
proto.getCountByCardId = function (huHandcard, cardId) {
    var count = 0;
    for (let i = 0; i < huHandcard.length; i++) {
        if (cardId == huHandcard[i]) {
            count++;
        }
    }
    return count;
};
//胡牌时玩家手牌中只要有三张风牌 或者 幺 九 花牌要+1
proto.getFengYJ = function (huHandcards, huCardId, laizi) {
    let flowerCount = 0;
    let cardId = [];
    logger.info("桌子id ==" + this.id + ":胡牌时的手牌getFengYJ = []", huHandcards);
    for (var i = 0; i < huHandcards.length; ++i) {
        let card = huHandcards[i];
        let count = this.getCountByCardId(huHandcards, card);
        //癞子不计算在内
        if (card == laizi) continue;
        // //可能是2对倒 胡的牌 是幺九 
        // if(card ==huCardId&&count>=2)
        //     count++;
        //已经检测过的就跳过吧
        let bAdded = false;
        for (let j = 0; j < cardId.length; j++) {
            if (card == cardId[j])
                bAdded = true;
        }
        if (bAdded) continue;
        if (card == laizi) continue;
        if ((card < 45 && card > 40) && count >= 3) {
            flowerCount++;
        }
        if ((card % 10 == 1 || card % 10 == 9) && count >= 3) {
            flowerCount++;
        }
        if (count >= 3) {
            cardId.push(card);
        }
    }
    return flowerCount;
}
//带癞子是否是7对
proto.is_7_pair = function (handCards, laizi, laiziCount) {
    //   logger.info("桌子id ==" + this.id + ":is_7_pair>>>laiziCount= %d  >> handCards = []",laiziCount,handCards);
    if (handCards.length <= CARDS_COUNT) return false;//手牌小于14张不能胡7小对
    handCards = this.sort(handCards);//排序
    var dcount = 0;
    var nTmp = handCards.length;
    for (var i = 0; i < nTmp; ++i) {
        if (handCards[i] == laizi) continue;//遇到癞子不判断
        if (i < nTmp - 1 && handCards[i] == handCards[i + 1]) {
            dcount++;
            i++;
        }
        else {
            if (laiziCount == 0) return false;
            laiziCount--;
            dcount++;
        }
    }
    if (laiziCount == 2) dcount++;
    //    logger.info("桌子id ==" + this.id + ":is_7_pair>>> dcount = %d",dcount);
    return (dcount == 7);
};
//判断将牌
proto.EnumerateJong = function (handCards/*除过癞子*/, laiziArr) {
    var arrJiang = [];
    var nJiang = 0;
    for (var i = 0; i < handCards.length - 1; ++i) {
        let jiang = [];
        if (handCards[i] == handCards[i + 1]) {
            jiang.push(handCards[i]);
            jiang.push(handCards[i + 1]);
            nJiang++;
        }
        else {
            if (laiziArr.length > 0) {
                jiang.push(handCards[i]);
                jiang.push(laiziArr[0]);
                nJiang++;
            }
        }
        if (jiang.length > 0) {
            arrJiang.push(jiang);
        }
    }
    if (laiziArr.length > 0) {
        let jang = [];
        jang.push(handCards[handCards.length - 1]);
        jang.push(laiziArr[0]);
        nJiang++;
        arrJiang.push(jang);
    }
    if (laiziArr.length > 1) {
        let jang = [];
        jang.push(laiziArr[0]);
        jang.push(laiziArr[0]);
        nJiang++;
        arrJiang.push(jang);
    }
    return { nJiang: nJiang, arrJiang: arrJiang };
}
/*
检测胡牌
cnNormalStone->除过癞子的所有牌的张数
cnGrouped->吃碰杠的数量
sHuResult->胡牌的类型
*/
proto.CheckNormal = function (hand, laizi, state) {
    var laiziArr = [];
    var handcards = [];
    for (let i = 0; i < hand.length; ++i) {
        //癞子和普通牌分开
        if (hand[i] == laizi) {
            laiziArr.push(hand[i]);
        }
        else {
            handcards.push(hand[i]);
        }
    }
    var laizicount = this.getLaiziCount(hand, laizi);
    if (state) {
        laiziArr = laiziArr.splice(0, 1);
        handcards.push(laiziArr[0]);
        laizicount--;
    }
    if (this.is_7_pair(hand, laizi, laizicount)) {
        return true;
    }
    handcards = this.sort(handcards);
    var anJongIndex = this.EnumerateJong(handcards, laiziArr);
    //    logger.info("桌子id ==" + this.id + ":CheckNormal >>>> anJongIndex >>>> ",anJongIndex);
    if (anJongIndex.nJiang == 0) {
        return false;
    }
    var cnLeftStone = 0;//剩下的牌数
    var cnLeftHun = 0;//剩下的癞子数
    for (let i = 0; i < anJongIndex.nJiang; ++i) {
        let asStoneTmp = handcards.slice(0, handcards.length);
        if (anJongIndex.arrJiang[i][1] == laiziArr[0]) {
            //将的第一张牌是癞子
            if (anJongIndex.arrJiang[i][0] == laiziArr[0]) {
                //将的第二张是癞子
                cnLeftStone = asStoneTmp.length;
                cnLeftHun = laiziArr.length - 2;
            }
            else {
                cnLeftStone = asStoneTmp.length - 1;
                cnLeftHun = laiziArr.length - 1;
                this.removeByValue(asStoneTmp, anJongIndex.arrJiang[i][0]);
            }
        }
        else {
            //两张都不是癞子
            cnLeftStone = asStoneTmp.length - 2;
            cnLeftHun = laiziArr.length;
            this.removeByValue(asStoneTmp, anJongIndex.arrJiang[i][0]);
            this.removeByValue(asStoneTmp, anJongIndex.arrJiang[i][1]);
        }
        if (this.CreateTree(asStoneTmp, cnLeftStone, cnLeftHun)) {
            return true;;
        }
    }
    return false;
}
/*
创建四叉树，用来判胡
handcards->除过将和癞子以外的所有手牌
cnNormalStone->除过将和癞子后的手牌数量
cnHun->癞子数量
*/
proto.CreateTree = function (handcards, cnNormalStone, cnHun) {
    //    logger.info("桌子id ==" + this.id + ":CreateTree>>>>cnNormalStone = %d,cnHun = %d",cnNormalStone,cnHun);
    if (cnNormalStone + cnHun > 14) {
        return false;
    }
    if (cnNormalStone + cnHun == 0) {
        //单钓
        return true;
    }
    if (cnNormalStone == 0) {
        if (cnHun % 3 != 0) {
            return false;
        }
        //除过将后剩下的都是癞子
        return true;
    }
    var res = this.CreateLeftChild(0, 0, handcards, cnNormalStone, cnHun);
    res |= this.CreateLeftChild(0, 1, handcards, cnNormalStone, cnHun);
    res |= this.CreateLeftChild(0, 2, handcards, cnNormalStone, cnHun);
    res |= this.CreateRightChild(0, handcards, cnNormalStone, cnHun);
    return res;
}
/*
构造左边的三棵子树( 顺 )，nChild指明构造第几棵子树
第n棵子树取第一个牌作为顺的第n张
nParentIndex->上一层结点下标
*/
proto.CreateLeftChild = function (nParentIndex, nChild, asStone, cnNormalStone, cnHun) {
    var asStoneTmp = asStone.slice(0, asStone.length);
    if (cnNormalStone == 0 || (cnNormalStone + cnHun) % 3 != 0) {
        //      logger.info("桌子id ==" + this.id + ":cnNormalStone == 0 || ( cnNormalStone + cnHun ) % 3 != 0,构造左边的三棵子树( 顺 ) 失败");
        return false;
    }
    if (this.getType(asStone[0]) > 3) {
        //风 字不能做顺子
        //     logger.info("桌子id ==" + this.id + ":字不能做顺子,构造左边的三棵子树( 顺 ) 失败");
        return false;
    }
    // 当前结点下标
    var nIndex = (nParentIndex << 2) + nChild + 1;
    var asSpareStone = [];//用来存剩余的牌
    var asNode = [];//存组好的牌
    var res = this.CreateShun(asStone, cnNormalStone, cnHun, nChild, asSpareStone);
    if (!res) {
        return false;
    }
    cnNormalStone = res.cnNormalStone;
    cnHun = res.cnHun;
    asSpareStone = res.asSpareStone;
    //这层节点创建成功
    if ((cnNormalStone + cnHun) % 3 != 0) {
        return false;
    }
    if (cnNormalStone + cnHun >= 3) {
        if (cnNormalStone == 0) {
            if (cnHun % 3 != 0) {
                return false;
            }
            return true;
        }
        var bSuccess = this.CreateLeftChild(nIndex, 0, asSpareStone, cnNormalStone, cnHun);
        bSuccess |= this.CreateLeftChild(nIndex, 1, asSpareStone, cnNormalStone, cnHun);
        bSuccess |= this.CreateLeftChild(nIndex, 2, asSpareStone, cnNormalStone, cnHun);
        bSuccess |= this.CreateRightChild(nIndex, asSpareStone, cnNormalStone, cnHun);
        //      logger.info("桌子id ==" + this.id + ":CreateLeftChild >>>> bSuccess = ",bSuccess);
        return bSuccess;
    }
    return true;
}
/*
构造右子树(刻)
*/
proto.CreateRightChild = function (nParentIndex, asStone, cnNormalStone, cnHun) {
    var asStoneTmp = asStone.slice(0, asStone.length);
    if (cnNormalStone == 0 || (cnNormalStone + cnHun) % 3 != 0) {
        return false;
    }
    var nIndex = (nParentIndex << 2) + 4;
    //   logger.info("桌子id ==" + this.id + ":CreateRightChild >>> nIndex = %d,",nIndex);
    var cnSameStone = 0;
    var sGroup = [];
    for (let i = 0; i < cnNormalStone; ++i) {
        //查找刻子
        if (asStone[0] != asStone[i]) {
            break;
        }
        cnSameStone++;
        sGroup.push(asStone[i]);
        this.removeByValue(asStoneTmp, asStone[i]);
        if (cnSameStone == 3) {
            break;
        }
    }
    var res = this.PerfectGroup(sGroup, cnHun);
    if (!res) {
        //检测数组是否完整（3*n）有癞子用癞子补缺
        return false;
    }
    cnHun = res.cnHun;
    //   logger.info("桌子id ==" + this.id + ":CreateRightChild >>>cnHun = %d,",cnHun);
    //本层节点创建成功
    cnNormalStone -= cnSameStone;
    if ((cnNormalStone + cnHun) % 3 != 0) {
        return false;
    }
    if (cnNormalStone + cnHun >= 3) {
        if (cnNormalStone == 0) {
            if (cnHun != 3) {
                return false;
            }
            return true;
        }
        var bSuccess = this.CreateLeftChild(nIndex, 0, asStoneTmp, cnNormalStone, cnHun);
        bSuccess |= this.CreateLeftChild(nIndex, 1, asStoneTmp, cnNormalStone, cnHun);
        bSuccess |= this.CreateLeftChild(nIndex, 2, asStoneTmp, cnNormalStone, cnHun);
        bSuccess |= this.CreateRightChild(nIndex, asStoneTmp, cnNormalStone, cnHun);
        //     logger.info("桌子id ==" + this.id + ":CreateRightChild >>>> bSuccess = ",bSuccess);
        return bSuccess;
    }
    return true;
}
/*
// 给定一个牌数组，以这个牌数组的第一张牌为基础，构造一个顺牌分组，nBaseIndex指明第一张牌
// 在这个分组里要放置的位置
*/
proto.CreateShun = function (asStone, cnNormalStone, cnHun, nBaseIndex, asSpareStone) {
    if (cnNormalStone <= 0 || nBaseIndex < 0 || nBaseIndex >= 3) {
        //       logger.info("桌子id ==" + this.id + ":CreateShun >>>> cnNormalStone <= 0 || nBaseIndex < 0 || nBaseIndex >= 3");
        return false;
    }
    if (nBaseIndex > cnHun) {
        //       logger.info("桌子id ==" + this.id + ":CreateShun >>>>  nBaseIndex > cnHun");
        // nBaseIndex == 2 则要求 cnHun >= 2，nBaseIndex == 1 则要求 cnHun >= 1
        return false;
    }
    if (this.getValue(asStone[0]) == 8 && nBaseIndex == 0 // 第一张牌是8，不能作顺的第一张
        || this.getValue(asStone[0]) == 9 && nBaseIndex < 2// 第一张牌是9，只能作顺的第三张 
        || this.getValue(asStone[0]) == 1 && nBaseIndex > 0// 第一张牌是1，只能作顺的第一张 
        || this.getValue(asStone[0]) == 2 && nBaseIndex == 2)// 第一张牌是2，不能作顺的第三张
    {
        //       logger.info("桌子id ==" + this.id + ":CreateShun >>>>  第一张牌是8，不能作顺的第一张......");
        return false;
    }
    asSpareStone = asStone.slice(0, asStone.length);
    //   logger.info("桌子id ==" + this.id + ":CreateShun >> asSpareStone",asSpareStone);
    var sGroup = [];//存一组顺子
    if (nBaseIndex != 2) {
        for (let i = 0; i < cnNormalStone; ++i) {
            var nDiff = asStone[i] - asStone[0];
            if (nDiff <= 2) {
                //找顺子
                if (sGroup.length == 0) {
                    //加入第一张牌
                    sGroup.push(asStone[i]);
                    this.removeByValue(asSpareStone, asStone[i]);
                }
                else {
                    //顺子中没有这张牌，则把这张牌加入顺子数组
                    if (!this.FindValue(sGroup, asStone[i])) {
                        sGroup.push(asStone[i]);
                        this.removeByValue(asSpareStone, asStone[i]);
                    }
                }
            }
            else {
                break;
            }
        }
    }
    else {
        //如果这张牌作为顺子的第三张牌，直接用癞子补缺
        sGroup.push(asStone[0]);
        this.removeByValue(asSpareStone, asStone[0]);
    }
    var cnScrapHun = cnHun;	// 剩下的混数
    ///  logger.info("桌子id ==" + this.id + ":CreateShun >> sGroup",sGroup);
    var res = this.PerfectGroup(sGroup, cnScrapHun);
    if (!res) {
        // 失败，这个分组无法填充完整
        return false;
    }
    cnNormalStone = cnNormalStone - 3 + cnHun - res.cnHun;
    cnHun = res.cnHun;
    return { cnHun: cnHun, cnNormalStone: cnNormalStone, asSpareStone: asSpareStone };
}
/*
 是否一个完备的分组，如果不完备，在需要的地方补充混
asHun->癞子数组
*/
proto.PerfectGroup = function (sGroup, cnHun) {
    //   logger.info("桌子id ==" + this.id + ":PerfectGroup >> cnHun = %d , sGroup=[]",cnHun,sGroup);
    if (sGroup.length == 0) {
        return false;
    }
    else if (sGroup.length == 1) {
        if (cnHun < 2) return false;
        else cnHun -= 2;
    }
    else if (sGroup.length == 2) {
        if (cnHun < 1) return false;
        else cnHun -= 1;
    }
    return { cnHun: cnHun };
}
//删除手牌中指定牌
proto.removeByValue = function (arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == val) {
            arr.splice(i, 1);
            break;
        }
    }
}
//查找手牌中指定的牌
proto.FindValue = function (arr, val) {
    for (let i = 0; i < arr.length; ++i) {
        if (arr[i] == val) {
            return true;
        }
    }
    return false;
}